package com.sun.huazhi.aitaskservice.service.impl;

import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.*;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;

import com.fasterxml.jackson.databind.ObjectMapper;
import com.mongodb.BasicDBObject;
import com.mongodb.DBObject;
import com.mongodb.client.gridfs.model.GridFSFile;
import com.sun.huazhi.aitaskservice.DTO.request.DynamicVideoRequestDTO;
import com.sun.huazhi.aitaskservice.common.Enum.TasksStatus;
import com.sun.huazhi.aitaskservice.dao.entity.VideoTasksDO;
import com.sun.huazhi.aitaskservice.dao.mapper.ApiLogsMapper;
import com.sun.huazhi.aitaskservice.dao.mapper.VideoTasksMapper;
import com.sun.huazhi.aitaskservice.service.DynamicVideoService;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.bson.types.ObjectId;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.cache.annotation.CacheEvict;
import org.springframework.cache.annotation.Cacheable;

import org.springframework.data.mongodb.core.query.Criteria;
import org.springframework.data.mongodb.core.query.Query;
import org.springframework.data.mongodb.gridfs.GridFsTemplate;
import org.springframework.retry.annotation.Backoff;
import org.springframework.retry.annotation.Retryable;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;

import java.time.LocalDateTime;
import java.util.stream.Collectors;

import cn.hutool.core.lang.Snowflake;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.support.TransactionSynchronization;
import org.springframework.transaction.support.TransactionSynchronizationManager;

@Service
@RequiredArgsConstructor
@Slf4j
public class DynamicVideoServiceImpl implements DynamicVideoService {

    private final ApiLogsMapper apiLogsMapper;
    private final VideoTasksMapper videoTasksMapper;
    private final RabbitTemplate rabbitTemplate;
    private final ObjectMapper objectMapper = new ObjectMapper();

    // 新增GridFS依赖
    private final GridFsTemplate gridFsTemplate;

    //注入Snowflake（需在配置类中初始化）
    private final Snowflake snowflake;

    /**
     * 事务性视频任务创建流程
     * @param requestParam 请求参数 传入userId和用户描述prompt
     *                     主要包含：数据库操作、日志记录和事务提交后发送消息三个核心部分
     * @return taskId 视频Id
     */
    @Override
    @Transactional
    public String createTask(DynamicVideoRequestDTO requestParam) {
        try{
            //TODO: 这里是根据用户输入的requestParam中的string类型进行生成视频 需要考虑如何提取用户输入关键信息

            //  taskId 生成方法 1:      String taskId = UUID.randomUUID().toString();
            // 方法2: 使用Snowflake生成分布式唯一Id（返回Long型，可转String）  注意snowflake的配置类config实现 和配置文件
            String taskId = snowflake.nextIdStr(); // 直接生成字符串ID

            VideoTasksDO task = new VideoTasksDO();
            task.setTaskId(taskId);
            task.setUserId(requestParam.getUserId());
            task.setStatus(TasksStatus.PENDING.name()); //初始状态
            task.setPrompt(requestParam.getPrompt());
            //task.setCreatedTime(null); //创建时间由后续的消费者填充 具体见VideoTaskConsumer代码
            task.setVideoFormat("mp4"); // 默认格式
            videoTasksMapper.insert(task);//存入SQL
//
//            ApiLogsDO apilog = new ApiLogsDO();
//            apilog.setUserId(requestParam.getUserId());
//            apilog.setApiType("dynamic_video_create");
//            apilog.setCreatedTime(LocalDateTime.now());
//            apilog.setResponseCode(200);
//            try {
//                apilog.setRequestData(objectMapper.writeValueAsString(requestParam));
//            } catch (JsonProcessingException e) {
//                apilog.setRequestData("{}");
//            }
//            apiLogsMapper.insert(apilog);

//            try {
//                apiLogsMapper.insert(apilog);
//            } catch (Exception e) {
//                // 打印完整异常信息（包括被包装的内部异常）
//                log.error("插入日志失败: ", e);  // 注意这里用 `, e` 而不是 `e.getMessage()`
//                throw new RuntimeException("数据库操作失败", e);
//            }

            TransactionSynchronizationManager.registerSynchronization(
                    new TransactionSynchronization() {
                        @Override
                        //通过 afterCommit() 确保消息仅在事务成功提交后发送
                        public void afterCommit() {
                            Map<String, Object> message = new HashMap<>();
                            message.put("taskId", taskId);
                            message.put("prompt", requestParam.getPrompt());
                            message.put("userId", requestParam.getUserId());
                            rabbitTemplate.convertAndSend(
                                    "video.task.exchange",
                                    "video.task.create",
                                    message
                            );
                        }
                    }
            );
            return taskId;
        }catch(Exception  e){
            log.error("创建视频任务失败", e);
            throw new RuntimeException("创建视频任务失败", e);
        }
    }

    // 查询方法增加Redis缓存
    //Spring Cache 通过 AOP 实现方法级别的缓存
        //方法调用前：检查缓存中是否存在以指定 key 为键的值
        //命中缓存：直接返回缓存值，不执行方法体
        //未命中缓存：执行方法，将返回值存入缓存（可配置过期时间）
    @Override
    @Cacheable(value = "videoTask", key = "#taskId") // 自动缓存
    public String getStatus(String taskId) {
        VideoTasksDO task = videoTasksMapper.selectOne(
                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
        );
        return task != null ? task.getStatus() : "not_found";
    }

//    @Override
//    public String getVideoUrl(String taskId) {
//        VideoTasksDO task = videoTaskMapper.selectOne(
//                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
//        );
//        return task != null ? task.getVideoUrl() : null;
//    }

    // 获取视频URL
    @Override
    @Cacheable(value = "videoUrls", key = "#taskId")
    public String getVideoUrl(String taskId) {
        VideoTasksDO task = videoTasksMapper.selectOne(
                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
        );
        return task != null ?
                "/api/videos/download/" + task.getGridFsFileId() : null;
    }
    // 获取用户视频历史记录
    @Override
    public List<VideoTasksDO> getUserHistory(Long userId) {
        return videoTasksMapper.selectList(
                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getUserId, userId).orderByDesc(VideoTasksDO::getCreatedTime)
        );
    }

    //删除视频
    //    @Override
//    public void deleteTask(String taskId) {
//        videoTaskMapper.delete(
//                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
//        );
//    }

    // 删除方法（双存储清理）
    @Override
    @Transactional
    @CacheEvict(value = {"videoTask", "videoUrls"}, key = "#taskId") //// 添加缓存清除逻辑
    public void deleteTask(String taskId) {
        // 1. 查询任务记录
        VideoTasksDO task = videoTasksMapper.selectOne(
                new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
        );

        if (task != null) {
            // 2. 删除GridFS文件
            if (task.getGridFsFileId() != null) {
                gridFsTemplate.delete(new Query(Criteria.where("_id").is(task.getGridFsFileId())));
            }

            // 3. 删除MySQL记录
            videoTasksMapper.deleteById(task.getId());
        }
    }

    // 视频存储方法（由消费者调用）
    // 在storeVideoToGridFS方法添加重试机制
    @Retryable(value = IOException.class, maxAttempts = 3, backoff = @Backoff(delay = 1000))
    @Transactional(propagation = Propagation.NOT_SUPPORTED) // 非事务
    public void storeVideoToGridFS(String taskId, File videoFile) {
        VideoTasksDO task = null;
        try {
            // 1. 查询MySQL任务记录
            task = videoTasksMapper.selectOne(
                    new LambdaQueryWrapper<VideoTasksDO>().eq(VideoTasksDO::getTaskId, taskId)
            );

            // 2. 存储到GridFS
            try (InputStream is = new FileInputStream(videoFile)) {
                DBObject metadata = new BasicDBObject();
                metadata.put("taskId", taskId);
                metadata.put("userId", task.getUserId());

                ObjectId fileId = gridFsTemplate.store(
                        is,
                        generateFilename(task), // 生成唯一文件名
                        "video/" + task.getVideoFormat(),
                        metadata
                );

                // 3. 更新MySQL记录
                task.setGridFsFileId(fileId.toString());
                task.setStatus(TasksStatus.COMPLETED.name());
                task.setCreatedTime(LocalDateTime.now());
                videoTasksMapper.updateById(task);
            }

        } catch (Exception e) {
            log.error("视频存储失败: {}", taskId, e);
            //updateTaskStatus(taskId, TasksStatus.FAILED);
            task.setStatus(TasksStatus.FAILED.name());
            videoTasksMapper.updateById(task);

        } finally {
            // 清理临时文件
            if (videoFile.exists()) videoFile.delete();
        }
    }

    private String generateFilename(VideoTasksDO task) {
        return String.format("video-%s-%d.%s",
                task.getTaskId(),
                System.currentTimeMillis(),
                task.getVideoFormat()
        );
    }
    // 定时清理未完成的任务文件
    @Scheduled(cron = "0 0 3 * * ?")
    public void cleanupOrphanFiles() {
        try {
            log.info("开始执行GridFS孤立视频文件清理任务");

            // 1. 获取所有MySQL中已有的GridFS文件ID（非空）
            List<String> validIds = videoTasksMapper.selectList(
                            new LambdaQueryWrapper<VideoTasksDO>()
                                    .select(VideoTasksDO::getGridFsFileId)
                                    .isNotNull(VideoTasksDO::getGridFsFileId)
                    ).stream()
                    .map(VideoTasksDO::getGridFsFileId)
                    .collect(Collectors.toList());

            Set<String> validIdSet = new HashSet<>(validIds); // 提高查询效率

            // 2. 查询MongoDB GridFS中所有文件
            List<GridFSFile> allFiles = new ArrayList<>();
            gridFsTemplate.find(new Query()).forEach(allFiles::add);

            int deleteCount = 0;
            // 3. 遍历所有GridFS文件，删除那些不在MySQL中记录的
            for (GridFSFile file : allFiles) {
                String fileId = file.getObjectId().toHexString();
                if (!validIdSet.contains(fileId)) {
                    gridFsTemplate.delete(new Query(Criteria.where("_id").is(file.getObjectId())));
                    deleteCount++;
                    log.info("删除无效GridFS文件：{}", fileId);
                }
            }

            log.info("GridFS清理完成，共删除 {} 个孤立文件", deleteCount);
        } catch (Exception e) {
            log.error("清理GridFS孤立文件失败", e);
        }
    }
}
